package model

// by daoyi
import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	set "gopkg.in/fatih/set.v0"
	"gorm.io/gorm"
	"net"
	"net/http"
	"strconv"
	"sync"
)

type Message struct {
	gorm.Model
	FormId   int64  //发送者
	TargetId int64  //接收者
	Type     int    //消息类型  群聊 私聊 广播
	Media    int    //消息类型  文字  图片  音频
	Content  string //消息内容
	Pic      string //消息类型为图片什么的，存地址
	Url      string //消息类型为 音频什么的，存地址
	Desc     string //消息类型为文字 字符串
	Amount   int    //其他数字统计
}

type Node struct {
	Conn      *websocket.Conn //websocket链接
	DataQueue chan []byte     //消息队列 管道管理消息队列
	GroupSets set.Interface   //集合   （set 并集 交集  差集）  线程安全集合
}

// 映射关系
var clientMap map[int64]*Node = make(map[int64]*Node, 0)

// 读写锁
var rwLocker sync.RWMutex

/**
 * 初始化
 */
func init() {
	go udpsendProc() //udp 数据发送协程
	go udpRecvProc() //udp 数据接收协程
	fmt.Println("init start >>>>>>>>>>>>")
}

// 需要 ：发送者ID，接收者ID 消息内容，消息类型，发送类型
func Chat(writer http.ResponseWriter, request *http.Request) {
	//1.效验token 合法性
	query := request.URL.Query()
	uId := query.Get("userId")
	userId, _ := strconv.ParseInt(uId, 10, 64)
	//targetId := query.Get("targetId")
	//context := query.Get("context")
	//msgType := query.Get("type")
	isvalida := true //checkToken()  效验token
	conn, err := (&websocket.Upgrader{
		//效验token
		CheckOrigin: func(r *http.Request) bool {
			return isvalida
		},
	}).Upgrade(writer, request, nil)
	if err != nil {
		fmt.Println(err)
		return
	}
	//2.获取conn
	node := &Node{
		Conn:      conn,
		DataQueue: make(chan []byte, 50),
		GroupSets: set.New(set.ThreadSafe), //线程安全
	}
	//3.用户关系
	//4.userid跟node绑定
	rwLocker.Lock()
	clientMap[userId] = node
	rwLocker.Unlock()
	//5.完成发送逻辑 定义一个协程完成功能
	go sendProc(node)
	//6.完成接收逻辑 定义一个协程完成功能
	go recvProc(node)
	sendMsg(userId, []byte("欢迎进入聊天软件"))
}

func sendProc(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			fmt.Println("[ws]sendProc >>>> msg:", string(data))
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

func recvProc(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			fmt.Println(err)
			return
		}
		boroadMsg(data)
		fmt.Println("[ws] <<<<<", data)
	}
}

var udpsendChan chan []byte = make(chan []byte, 1024)

func boroadMsg(data []byte) {
	udpsendChan <- data
}

// 完成udp 数据发送协程
func udpsendProc() {
	con, err := net.DialUDP("udp", nil, &net.UDPAddr{
		// IP:   net.IPv4(192, 168, 169, 230),
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 3000,
	})
	defer con.Close()
	if err != nil {
		fmt.Println(err)
	}

	for {
		select {
		case data := <-udpsendChan:
			fmt.Println("udpsendProc data :", string(data))
			_, err := con.Write(data)
			if err != nil {
				fmt.Println(err)
				return
			}
		}
	}
}

// 完成udp 数据接收协程
func udpRecvProc() {
	con, err := net.ListenUDP("udp", &net.UDPAddr{
		//IP:   net.IPv4zero,
		// IP:   net.IPv4(192, 168, 169, 230),
		IP:   net.IPv4(0, 0, 0, 0),
		Port: 3000,
	})
	if err != nil {
		fmt.Println(err)
	}
	defer con.Close()
	for {
		var buf [512]byte
		n, err := con.Read(buf[0:]) //接收数据
		if err != nil {
			fmt.Println(err)
			return
		}
		fmt.Println("udpRecvProc data :", string(buf[0:n]))
		dispatch(buf[0:n])
	}
}

// 后端调度逻辑
func dispatch(data []byte) {
	msg := Message{}
	fmt.Println("dispatch data :", string(data))
	err := json.Unmarshal(data, &msg)
	if err != nil {
		fmt.Println(err)
		return
	}
	switch msg.Type {
	case 1: //私信
		sendMsg(msg.TargetId, data)
		//case 2: //群发
		//	sendGroupMsg()
		//case 3:
		//	sendAllMsg()
		//case 4:

	}
}

func sendMsg(userId int64, msg []byte) {
	fmt.Println("sendMsg >>> userId :", userId, " msg:", string(msg))
	rwLocker.RLock()
	node, ok := clientMap[userId]
	rwLocker.RUnlock()
	if ok {
		//将消息放入DataQueue队列中
		node.DataQueue <- msg
	}
}
